//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Reflection;
using System.Windows.Input;
using OpenLS.Core.Notification;

namespace OpenLS.Spreadsheet.UI
{
    /// <summary>
    /// Interaction logic for FindAndReplaceWindow.xaml
    /// </summary>
    public class FindAndReplace : ViewModelBase
    {
        private Cell _currentCell;
        private ActionCommand _findAllCommand;
        private ActionCommand _findNextCommand;
        private SearchLocation _location;
        private bool _matchCase;
        private bool _matchEntireCellContent;
        private List<Cell> _matchingCells;
        private FindAndReplaceMode _mode;
        private ActionCommand _replaceAllCommand;
        private ActionCommand _replaceCommand;
        private SearchBy _searchBy;
        private SearchIn _searchIn;
        private string _toFind = string.Empty;
        private string _toReplace = string.Empty;

        public FindAndReplace(Worksheet worksheet)
        {
            Worksheet = worksheet;
        }

        public ICommand ReplaceAllCommand
        {
            get
            {
                if (_replaceAllCommand == null)
                    _replaceAllCommand = new ActionCommand(parameter => replaceAll());
                return _replaceAllCommand;
            }
        }

        public ICommand FindAllCommand
        {
            get
            {
                if (_findAllCommand == null)
                    _findAllCommand = new ActionCommand(parameter => findAll());
                return _findAllCommand;
            }
        }

        public ICommand FindNextCommand
        {
            get
            {
                if (_findNextCommand == null)
                    _findNextCommand = new ActionCommand(parameter => findNext());
                return _findNextCommand;
            }
        }

        public ICommand ReplaceCommand
        {
            get
            {
                if (_replaceCommand == null)
                    _replaceCommand = new ActionCommand(parameter => replace());
                return _replaceCommand;
            }
        }

        public bool MatchCase
        {
            get { return _matchCase; }
            set
            {
                _matchCase = value;
                notifyPropertyChanged("MatchCase");
            }
        }

        public bool MatchEntireCellContent
        {
            get { return _matchEntireCellContent; }
            set
            {
                _matchEntireCellContent = value;
                notifyPropertyChanged("MatchEntireCellContent");
            }
        }

        public string ToFind
        {
            get { return _toFind; }
            set
            {
                _toFind = value;
                notifyPropertyChanged("ToFind");
            }
        }

        public string ToReplace
        {
            get { return _toReplace; }
            set
            {
                _toReplace = value;
                notifyPropertyChanged("ToReplace");
            }
        }


        public FindAndReplaceMode Mode
        {
            get { return _mode; }
            set
            {
                _mode = value;
                notifyPropertyChanged("Mode");
            }
        }

        public SearchLocation Location
        {
            get { return _location; }
            set
            {
                _location = value;
                notifyPropertyChanged("Location");
            }
        }

        public SearchIn SearchIn
        {
            get { return _searchIn; }
            set
            {
                _searchIn = value;
                notifyPropertyChanged("SearchIn");
            }
        }

        public SearchBy SearchBy
        {
            get { return _searchBy; }
            set
            {
                _searchBy = value;
                notifyPropertyChanged("SearchBy");
            }
        }

        public List<Cell> MatchingCells
        {
            get { return _matchingCells; }
            set
            {
                _matchingCells = value;
                if (_matchingCells != null && _matchingCells.Count > 0)
                    CurrentCell = _matchingCells[0];
                notifyPropertyChanged("MatchingCells");
            }
        }

        public Cell CurrentCell
        {
            get { return _currentCell; }
            set
            {
                _currentCell = value;
                notifyPropertyChanged("CurrentCell");
                if (_currentCell == null)
                    return;
                _currentCell.Select();
                Worksheet.TopLeftPane.EnsureVisible(CurrentCell);
            }
        }

        public Worksheet Worksheet { get; private set; }

     
        private void findNext()
        {
            if (MatchingCells == null)
            {
                MatchingCells = new List<Cell>(getMatchingCells());
            }
            if (CurrentCell == null)
                return;
            int index = MatchingCells.IndexOf(CurrentCell);
            CurrentCell = index < MatchingCells.Count - 1 ? MatchingCells[index + 1] : null;
        }

        private void replace()
        {
            if (MatchingCells == null)
            {
                MatchingCells = new List<Cell>(getMatchingCells());
            }
            if (CurrentCell == null)
                return;
            if (CurrentCell != null)
                CurrentCell.Formula = CurrentCell.FormattedValue.Replace(ToFind, ToReplace);
            int index = MatchingCells.IndexOf(CurrentCell);
            CurrentCell = index < MatchingCells.Count - 1 ? MatchingCells[index + 1] : null;
        }

        private void replaceAll()
        {
            findAll();
            foreach (Cell c in MatchingCells)
                c.Formula = c.FormattedValue.Replace(ToFind, ToReplace);
        }

        private Comparison<string> getStringComparison()
        {
            if (MatchCase)
            {
                if (MatchEntireCellContent)
                {
                    return (s1, s2) => string.Compare(s1, s2);
                }
                return delegate(string s1, string s2)
                           {
                               if (s1 == null)
                                   return -1;
                               if (s1.Contains(s2))
                                   return 0;
                               return 1;
                           };
            }
            if (MatchEntireCellContent)
            {
                return (s1, s2) => string.Compare(s1, s2, true);
            }
            return delegate(string s1, string s2)
                       {
                           if (s1 == null)
                               return -1;
                           if (s2 == null)
                               return 1;
                           if (s1.ToLower().Contains(s2.ToLower()))
                               return 0;
                           return 1;
                       };
        }


        private Predicate<Cell> getCellPredicate()
        {
            Comparison<string> comparison = getStringComparison();
            string value = ToFind; //\\ ToFind.Text;
            if (Mode == FindAndReplaceMode.Find)
            {
                switch (SearchIn)
                {
                    case SearchIn.Formulas:
                        return c => comparison(c.Formula, value) == 0 || (comparison(c.FormattedValue, value) == 0);
                    case SearchIn.Values:
                        return c =>
                                   {
                                       object v = c.Value;
                                       if (v == null)
                                           return false;
                                       return comparison(v.ToString(), value) == 0;
                                   };
                    case SearchIn.Comment:
                        return delegate(Cell cell)
                                   {
                                       Comment comment = cell.Comment;
                                       if (comment == null)
                                           return false;
                                       return comparison(comment.InternalText, value) == 0;
                                   };
                    default:
                        throw new NotSupportedException();
                }
            }
            return c => comparison(c.Formula, value) == 0 || comparison(c.FormattedValue, value) == 0;
        }

        private void findAll()
        {
            MatchingCells = new List<Cell>(getMatchingCells());
        }

        private IEnumerable<Cell> getCells(Predicate<Cell> p)
        {
            switch (Location)
            {
                case SearchLocation.Worksheet:
                    case SearchLocation.Workbook:
                    foreach (Worksheet ws in getSearchedWorksheet())
                    {
                        foreach (Cell c in ws.GetCells())
                        {
                            if (p(c))
                                yield return c;
                        }
                    }
                    break;

                case SearchLocation.Selection:
                    foreach (var c in this.Worksheet.Selection.GetNonEmptyCells())
                    {
                        if (p(c))
                            yield return c;
                    }
                    break;
                default: throw new NotSupportedException();
            }
        }

        private IEnumerable<Cell> getMatchingCells()
        {
            return getCells(getCellPredicate());
        }

        private IEnumerable<Worksheet> getSearchedWorksheet()
        {
            var ws = Worksheet;
            switch (Location)
            {
                case SearchLocation.Workbook:
                    foreach (Worksheet s in ws.Workbook.Sheets)
                        yield return s;
                    break;

                case SearchLocation.Selection:
                    case SearchLocation.Worksheet:
                    yield return ws;
                    break;
                default: throw new NotSupportedException();

            }
        }
    }
}